package cos480.amiwsd;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Scanner;


/**
 * AddMgmt:
 * 
 * The Add manager. This class provides methods for adding the simulation results to the database. This is used by the PSSTulator.
 * 
 * @author will, emma
 *
 */

public class AddMgmt {
	
	private Charset charset;
	
	public AddMgmt(){
		// initialize charset
		charset = Charset.forName("US-ASCII");
	}
	
	public String addSimResult(Connection con, String s) throws SQLException {
		String fname = s;
		String[] splitLine;
		String line = null;
		String lastLine = null;
		Statement sql = con.createStatement();
		ResultSet rs;
		int s_id = -1;
		PreparedStatement pStatementInsert, pStatementQuery;
		int i;
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fname)), charset));
			
			// ignore beginning comments
			do{
				lastLine = line;
				line = reader.readLine();
			}while((line.equals("")) || (line.charAt(0) == '#') || (line.charAt(0) == '\n') || (line.charAt(0) == '\r'));
			
			// last commented line contains timestamp
			// TODO this query for s_id should really also use phi, mu, gamma
			String simType = fname.split("_")[1];
			pStatementQuery = con.prepareStatement("select s_id from simulation where p_0 = ? and p_1 = ? and p_2 = ? and type='"+simType+"'");
			pStatementInsert = con.prepareStatement("insert into simresult values (default, ?, '"+lastLine.substring(1)+"', ?, ?, ?, ?, ?, ?, ?, ?, ?, '"+fname+"')");
			do{
				splitLine = line.split("\t");
				
//				for(i = 0; i < splitLine.length; i++)
//					System.out.print("|"+splitLine[i]+ "|, ");
//				System.out.println();

				pStatementQuery.setDouble(1, Double.parseDouble(splitLine[1]));
				pStatementQuery.setDouble(2, Double.parseDouble(splitLine[2]));
				pStatementQuery.setDouble(3, Double.parseDouble(splitLine[3]));
				
				// REMOVE THIS -- FIX KLUGE!
				try{
					sql.executeUpdate("insert into simulation values (default, "+splitLine[1]+", "+splitLine[2]+", "+splitLine[3]+", 100, 1, 3, '" +simType+ "', 0, 1024, 10, 50, 100, 10, 'R1024_L0-params')");
				}catch(SQLException e){
					// DO NOTHING
				}
				
				rs = pStatementQuery.executeQuery();
				if(rs != null){
					if(rs.next()){
						s_id = rs.getInt(1);
					}
				}
				
				pStatementInsert.setInt(1, s_id);
				for(i = 2; i < splitLine.length-3; i++)
					pStatementInsert.setDouble(i,Double.parseDouble(splitLine[i+2]));
				try {
					pStatementInsert.executeUpdate();
				} catch (SQLException e) {
					System.err.println("Could not add simulation result: " + e.getMessage());
					throw e;
				}
			}while (((line = reader.readLine()) != null) && line.charAt(0) != '#');
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// users should not be able to store "add"s; that's weird
		return null;
	}
	
	public String addIPs(Statement sql, HashMap<String, String> pl) throws SQLException {
		int successStrLen = 10;	// offsets for parsing stuff from file names
		int failureStrLen = 9;
		int emptyStrLen = 15;
		int dateStrLen = 10;
		int scanID = -1;
		ResultSet rs;
		File tempFile;
		String fname, date = null;
		char success;
		if(pl.containsKey("filename"))
			fname = (pl.get("filename"));
		else{
			System.err.println("You must supply a filename from which to load scan results.");
			return null;
		}
		
		if(pl.containsKey("success"))
			success = pl.get("success").charAt(0);
		else{
			System.out.print("Using supplied filename to get success/failure/emptiness of scan results... ");
			if(fname.startsWith("emptyserverslist"))
				success = 'e';
			else if(fname.startsWith("serverslist"))
				success = 's';
			else if(fname.startsWith("errorslist"))
				success = 'f';
			else{
				System.err.println("You must supply the success of the scan results or use" +
								   " a filename that uses the proper naming conventions.");
				return null;
			}
		}
		
		if(pl.containsKey("date"))
			date = (pl.get("date"));
		else{
			System.out.print("Using supplied filename to get scan date... ");
			try{
				switch(success){
				case 's':
					date = fname.substring(successStrLen,successStrLen+dateStrLen);
					System.out.println("success date: " + date);
					break;
				case 'f':
					date = fname.substring(failureStrLen,failureStrLen+dateStrLen);
					System.out.println("failure date: " + date);
					break;
				case 'e':
					date = fname.substring(emptyStrLen,emptyStrLen+dateStrLen);
					System.out.println("empty date: " + date);
					break;
				default:
					System.err.println("You should never see this. Something has gone wrong " +
									   "in date parsing of scan results.");
					break;
				}
			} catch (IndexOutOfBoundsException e){
				System.err.println("You must supply the date of the scan results or use" +
								   " a filename that uses the proper naming conventions.");
				return null;
			}
		}
		
		String statement = "insert into scan values (default, '"+date+"', '"+success+"') returning scan_id";
		try{
			rs = sql.executeQuery(statement);
			if(rs.next())
				scanID = rs.getInt(1);
		} catch(SQLException e){
			System.err.println("unable to update scan table in database: " + e.getMessage());
			System.err.println("query: " + statement);
			return null;
		}
		
		// now we have to process the whole damn file before we read it in:
		// 1. to escape special characters: \ (backslash), : (colon)
		// 		yes, CR and/or LF in result data WILL cause problems)
		// 2. to add the scan ID to be read in so that results will be associated
		//		with the correct entry in the scan table
		// 3. add colons after non-timed-out IP addresses in errors (failed) scan lists
		System.out.println("Processing scan result file \""+fname+"\"...");
		tempFile = processScanResultFile(success, fname, scanID);
		
		// finally, copy the file into the database
		// TODO "new"
		System.out.println("Copying results from \""+fname+"\" into database...");
		try{
			switch(success){
			case 's':
			case 'f':
				// IP: <result string>:<scanID>
				//sql.executeUpdate("copy resultofscan_new from '" + tempFile.getAbsolutePath() + "' with delimiter as ':' null as ''");
				sql.executeUpdate("copy resultofscan from '" + tempFile.getAbsolutePath() + "' with delimiter as ':' null as ''");
				break;
			case 'e':
				// IP:<scanID>
				//sql.executeUpdate("copy resultofscan_new(ip,scan_id) from '" + tempFile.getAbsolutePath() + "' with delimiter as ':' null as ''");
				sql.executeUpdate("copy resultofscan(ip,scan_id) from '" + tempFile.getAbsolutePath() + "' with delimiter as ':' null as ''");
				break;
			}
		}catch(SQLException e){
			throw e;
		}
		
		// remove temporary scan result file
		tempFile.delete();
		
		// put all new hosts in host table
		// TODO "new"
		//sql.executeUpdate("insert into host_new(ip) ((select ip from resultofscan_new where scan_id="+scanID+")" +
		//				  "except (select ip from host_new))");
		sql.executeUpdate("insert into host(ip) ((select ip from resultofscan where scan_id="+scanID+")" +
				  "except (select ip from host))");

		
		// users should not be able to store "add"s; that's weird
		return null;
		
//		String fname, date, success, ip, result;
//		fname = (pl.get("filename") != null ? pl.get("filename") : "defaultFileName");
//		date = (pl.get("date") != null ? pl.get("date") : "2011:12:12"); //TODO: some dumb default date... fix this later....
//		success = (pl.get("success") != null ? pl.get("success") : "s");
//		String[] splitLine;
//		int count = 0;
//		Charset charset = Charset.forName("US-ASCII");
//		PreparedStatement pStatementHost, pStatementResult;
//		pStatementHost = con.prepareStatement("insert into host values(?::inet, default)");
//		
//		statement = "insert into resultofscan values('"+date+"', ?::inet, '"+success+"', ?)";
//		//statement += (result == null? "default)":"?)");
//		pStatementResult = con.prepareStatement(statement);
//		
//		try {
//			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fname)), charset));
//			String line = null;
//			while ((line = reader.readLine()) != null) {
//				//System.out.println(++count + ": " + line);
//				if(line.contains(": ")){
//					splitLine = line.split(": ", 2);
//					ip = splitLine[0];
//					// sort of clean the result text -- replace ' with ''
//					result = splitLine[1].replace("'", "''");
//				}
//				else{
//					ip = line;
//					result = null;
//				}
//				// first put IP in hosts if it is not already
//				// TODO create trigger (or something better?) to handle this
////				boolean IPinHosts = false;
//				//System.out.println(">>" + ip + "<<");
////				ResultSet rs = sql.executeQuery("select true from host where ip = '"+ ip +"'");
////				if(rs.next())
////					IPinHosts = true;
////				if(!IPinHosts)
////					sql.executeUpdate("insert into host values('" + ip + "', default)");
//
//				
////				try {
////					ResultSet rs = sql.executeQuery("select from host where ip = '" + ip + "'::inet");
////					if (rs.next()) {
////						String alloc = rs.getString(2);
////						if (alloc.equals("ALLOCVALUEHERE")) { //this should be the input value for the allocation
////							//update the allocation if the date is later than the last scan date of the IP address
////							
////						}
////					}
////				} catch (SQLException e) {
////					//some sort of failure here.... 
////				}
//				
//				try {
//					pStatementHost.setString(1, ip);
//					pStatementHost.executeUpdate(); //attempt to insert, it will fail if the IP is already in the table.
//				} catch (SQLException e) {
//					// unable to insert because IP address already in table, simply ignore the exception.
//				}
//				
//				
//				// then add it to ResultOfScan table				
//				try {
//					pStatementResult.setString(1, ip);
//					pStatementResult.setString(2, result);
//					pStatementResult.executeUpdate();
//				} catch (SQLException e) {
//					System.err.println("Failed to insert into the resultofscan table. Here's the query: \n");
//					System.err.println(statement);
//					throw e;
//				}
//				
//				
//			}
//			reader.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//	}
//
//	// users should not be able to store "add"s; that's weird
//	return null;
	}
	
	/**
	 * Processes a scan result file to prepare it to be copied into database. Specifically,
	 * depending on the success of the scan:
	 * 1. escape special characters: \ (backslash); : (colon)
	 * 	  yes, CR and/or LF in result data WILL cause problems)
	 * 2. add the scan ID to be read in so that results will be associated
	 *	  with the correct entry in the scan table
	 * 3. add colons after non-timed-out IP addresses in errors (failed) scan lists
	 * 
	 * @param success success of scan: e (empty); s (successful); f (failed)
	 * @param fname name of file to process
	 * @param scanID ID of scan to append to each line
	 */
	private File processScanResultFile(char success, String fname, int scanID){
		int i;
		String line;
		String[] splitLine;
		File tempFile = new File(fname+"_temp");
		try{
			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fname)), charset));
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempFile), charset));
			
			// adds scanID to all, plus other special things
			switch(success){
			case 's':
				// escape backslash, colons
				while ((line = reader.readLine()) != null) {
					splitLine = line.split("\\\\"); 
					if(splitLine.length > 1){
						line = "";
						for(i = 0; i < splitLine.length-1; i++)
							line += splitLine[i] + "\\\\";
						line += splitLine[i];
					}
					splitLine = line.split(":");
					line = splitLine[0] + ":" + splitLine[1].substring(1);
					if(splitLine.length > 2){
						for(i = 2; i < splitLine.length-1; i++)
							line += splitLine[i] + "\\:";
						line += splitLine[i];
					}
					line += ":" + scanID + "\n";
					writer.write(line);
				}
				break;
			case 'f':
				// add colon if necessary
				while ((line = reader.readLine()) != null) {
					if(!line.contains(":"))
						line += ":";
					else
						line = line.replaceAll(": ", ":");
					line += ":" + scanID + "\n";
					writer.write(line);
				}
				break;
			case 'e':
				// boring (good), just add scanID
				while ((line = reader.readLine()) != null) {
					line += ":" + scanID + "\n";
					writer.write(line);
				}
				break;
			}
			reader.close();
			writer.close();
		}catch (IOException e){
			System.err.println("Failed to process scan result file: " + e.getMessage());
			e.printStackTrace();
		}
	return tempFile;
	}
	
}
